package com.qm.console.service.jw;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.hash.Hashing;
import com.google.common.io.Files;
import com.qm.console.component.exception.BusinessException;
import com.qm.console.component.util.HttpInvokeUtil;
import com.qm.console.component.util.ZipUtil;
import com.qm.console.config.Config;
import com.qm.console.controller.dto.ResultEnum;
import com.qm.console.dao.dm.ProductProcessDOMapper;
import com.qm.console.model.ProductProcessDO;
import com.qm.console.model.ProductProcessDOExample;
import com.qm.console.pubdef.constant.UrlConstant;
import com.qm.console.pubdef.jw.bo.ProductBO;
import com.qm.console.pubdef.jw.dto.ProductQueryDTO;
import com.qm.console.utils.AliOssUtil;
import com.qm.console.utils.S3Util;
import com.qm.console.utils.VideoUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.util.TextUtils;
import org.apache.tomcat.util.http.fileupload.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;
import java.util.*;

/**
 * //                       _oo0oo_
 * //                      o8888888o
 * //                      88" . "88
 * //                      (| -_- |)
 * //                      0\  =  /0
 * //                    ___/`---'\___
 * //                  .' \\|     |// '.
 * //                 / \\|||  :  |||// \
 * //                / _||||| -:- |||||- \
 * //               |   | \\\  -  /// |   |
 * //               | \_|  ''\---/''  |_/ |
 * //               \  .-\__  '-'  ___/-. /
 * //             ___'. .'  /--.--\  `. .'___
 * //          ."" '<  `.___\_<|>_/___.' >' "".
 * //         | | :  `- \`.;`\ _ /`;.`/ - ` : | |
 * //         \  \ `_.   \_ __\ /__ _/   .-` /  /
 * //     =====`-.____`.___ \_____/___.-`___.-'=====
 * //                       `=---='
 * //
 * //
 * //     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * //
 * //               佛祖保佑         永无BUG
 * <p>
 * com.qm.console.service.jw
 * Created on 2021/7/22.
 */
@Slf4j
@Service
public class JWResourceService {

   @Autowired
   private ProductProcessDOMapper productProcessDOMapper;
   @Autowired
   private AliOssUtil aliOssUtil;

   @Autowired
   private S3Util s3Util;
   @Value("${spring.uploadFolder}")
   private String destFolder;
   @Autowired
   private ResourceLoader resourceLoader;
   private String defaultThumb;
   private Point defaultThumbSize;

   @PostConstruct
   private void uploadDefaultThumb() {
      Resource res = resourceLoader.getResource("classpath:play-button.png");
      try {
         defaultThumb = aliOssUtil.uploadImage(res.getFile(), "default_thumb.jpg");
         defaultThumbSize = aliOssUtil.imageSize(res.getFile());
      } catch (IOException e) {
      }
   }

   public List<ProductProcessDO> listProducts(ProductQueryDTO query) {
      return productProcessDOMapper.listProducts(query);
   }

   public int countProducts(ProductQueryDTO query) {
      return productProcessDOMapper.countProducts(query);
   }

   public void processProduct() {
      ProductProcessDOExample sample = new ProductProcessDOExample();
      sample.createCriteria().andStateEqualTo(Constants.PRODUCT_STATE_WAITING);
      List<ProductProcessDO> products = productProcessDOMapper.selectByExample(sample);
      products.forEach(this::processProduct);
   }

   public void retryProductUpload(int id) {
      ProductProcessDOExample sample = new ProductProcessDOExample();
      sample.createCriteria().andStateEqualTo(Constants.PRODUCT_STATE_FAILED).andIdEqualTo(id);
      ProductProcessDO item = new ProductProcessDO();
      item.setId(id);
      item.setState(Constants.PRODUCT_STATE_WAITING);
      productProcessDOMapper.updateByExampleSelective(item, sample);
   }

   public JSONObject processImageUpload(File imageFile) {
      boolean isRelease = StringUtils.equals(Config.getEnv(), "production");
      log.info("=======================config env:" + Config.getEnv() + ",release:" + isRelease);
      String key = isRelease ? s3Util.uploadImage(imageFile) : aliOssUtil.uploadImage(imageFile);
      Point size = isRelease ? s3Util.imageSize(imageFile) : aliOssUtil.imageSize(imageFile);
      JSONObject result = new JSONObject();
      result.put("url", key);
      result.put("width", size.x);
      result.put("height", size.y);
      imageFile.delete();
      return result;
   }

   public JSONObject processVideoUpload(File videoFile) {
      JSONObject result = new JSONObject();
      boolean isRelease = StringUtils.equals(Config.getEnv(), "production");
      String key = isRelease ? s3Util.uploadImage(videoFile) : aliOssUtil.uploadImage(videoFile);
      try {
         File thumbFile = new File(destFolder, key + ".thumb.jpg");
         VideoUtils.fetchPic(videoFile, thumbFile.getAbsolutePath());
         String thumbKey = defaultThumb;
         Point thumbSize = defaultThumbSize;
         if (thumbFile.exists()) {
            thumbKey = isRelease ? s3Util.uploadImage(thumbFile) : aliOssUtil.uploadImage(thumbFile);
         }
         result.put("thumb", thumbKey);
         result.put("url", key);
         return result;
      } catch (Exception e) {
         throw new BusinessException(ResultEnum.UPLOAD_FAILED_IO_EXCEPTION);
      }
   }

   public void processUpload(File zipFile) {
      if (!zipFile.exists()) {
         log.error("failed to find file to process: {}", zipFile);
         return;
      }
      String fileMd5 = "";
      try {
         fileMd5 = Files.asByteSource(zipFile).hash(Hashing.md5()).toString();
      } catch (IOException e) {
      }
      if (StringUtils.isEmpty(fileMd5)) {
         log.error("failed to get md5 checksum of file: {}", zipFile);
         return;
      }
      ProductProcessDOExample md5Example = new ProductProcessDOExample();
      md5Example.createCriteria().andFileCheckSumEqualTo(fileMd5);
      List<ProductProcessDO> current = productProcessDOMapper.selectByExample(md5Example);
      if (!current.isEmpty()) {
         log.error("duplicate file process, return");
         zipFile.delete();
         return;
      }
      File destDir = new File(zipFile.getAbsolutePath() + ".dir");
      try {
         ZipUtil.unzip(zipFile, destDir);
         zipFile.delete();
         preprocessDir(fileMd5, destDir);
      } catch (Exception e) {
         throw new BusinessException(ResultEnum.UPLOAD_FAILED_UNZIP_FAILED);
      }
   }

   private void preprocessDir(String md5, File dir) {
      if (!dir.exists()) {
         log.error("failed to find source folder: {}", dir);
         return;
      }
      File[] files = dir.listFiles();
      for (File product: files) {
         if (product.getName().contains("MACOS")) {
            // debug的时候会出现这个鬼勾8东西
            continue;
         }
         preprocessProduct(md5, product);
      }
   }

   private void preprocessProduct(String md5, File productDir) {
      ProductProcessDO productDO = new ProductProcessDO();
      productDO.setProductFolder(productDir.getAbsolutePath());
      productDO.setFileCheckSum(md5);
      productDO.setProduct(productDir.getName());
      saveProductProcess(productDO, Constants.PRODUCT_STATE_WAITING);
   }

   private void processProduct(ProductProcessDO productDO) {
      File productDir = new File(productDO.getProductFolder());
      File[] files = productDir.listFiles();
      if (files == null) {
         files = new File[0];
      }
      File txtFile = null;
      File bannerFile = null;
      File detailFile = null;
      for (File file : files) {
         if (file.isDirectory()) {
            if (StringUtils.equals(file.getName(), "banner")) {
               bannerFile = file;
            } else if (StringUtils.equals(file.getName(), "detail")) {
               detailFile = file;
            }
         } else {
            if (file.getName().endsWith(".txt")) {
               txtFile = file;
            }
         }
      }
      if (txtFile == null || bannerFile == null || detailFile == null) {
         saveProductProcess(productDO, Constants.PRODUCT_STATE_FAILED, String.format("文件结构错误, banner: %s, detail: %s, txt: %s",
                 bannerFile != null, detailFile != null, txtFile != null));
         return;
      }

      saveProductProcess(productDO, Constants.PRODUCT_STATE_PROCESSING);

      String classifyId = Files.getNameWithoutExtension(txtFile.getName());

      String content = processTxt(txtFile);
      if (content == null || content.isEmpty()) {
         saveProductProcess(productDO, Constants.PRODUCT_STATE_FAILED,"读取txt文件内容失败");
         return;
      }
      String banner = processImage(bannerFile);
      String detail = processImage(detailFile);

      if (StringUtils.isEmpty(banner)) {
         saveProductProcess(productDO, Constants.PRODUCT_STATE_FAILED, "没有banner图片");
         return;
      }
      if (StringUtils.isEmpty(detail)) {
         saveProductProcess(productDO, Constants.PRODUCT_STATE_FAILED, "没有detail图片");
         return;
      }

      ProductBO itemToUpload = ProductBO.builder()
              .classifyId(classifyId)
              .parameter(content)
              .pictures(banner)
              .detail(detail)
              .name(productDO.getProduct())
              .build();
      String url= UrlConstant.ApiServerHost+"/goods";

      int retry = 0;
      while (retry++ < 5) {
         String body = HttpInvokeUtil.httpPostJson(url, new HashMap<>(), (JSONObject) JSONObject.toJSON(itemToUpload), UrlConstant.DefaultEncode, null);
         if (StringUtils.isEmpty(body)) {
            continue;
         }
         JSONObject resp = JSONObject.parseObject(body);
         if (resp.getInteger("status") == 200) {
            saveProductProcess(productDO, Constants.PRODUCT_STATE_OK, "");
            try {
               FileUtils.deleteDirectory(productDir);
            } catch (IOException e) {
               log.error("failed to delete folder: {}", productDir);
            }
            return;
         }
      }
      saveProductProcess(productDO, Constants.PRODUCT_STATE_FAILED, "请求业务服务器失败.");
   }

   private void saveProductProcess(ProductProcessDO productDO, int state) {
      saveProductProcess(productDO, state, null);
   }

   private void saveProductProcess(ProductProcessDO productDO, int state, String reason) {
      productDO.setState(state);
      productDO.setReason(reason);
      if (productDO.getId() == null) {
         int id = productProcessDOMapper.insertSelective(productDO);
         productDO.setId(id);
      } else {
         productProcessDOMapper.updateByPrimaryKeySelective(productDO);
      }
   }


   private String processTxt(File file) {
      String content = null;
      try {
         content = Files.toString(file, Charset.forName("gbk"));
      } catch (IOException e) {
      }
      if (StringUtils.isEmpty(content)) {
         return null;
      }
      String[] lines = content.split("[\r\n|\n]");
      final HashMap<String, Integer> indexes = new HashMap<>();
      SortedMap<String, String> result = new TreeMap<>(new Comparator<String>() {
         @Override
         public int compare(String o1, String o2) {
            return indexes.get(o1) - indexes.get(o2);
         }
      });
      int i = 0;
      for (String line : lines) {
         int offset = 0;
         String key = "";
         String value = "";
         for(int j = 0; j < line.length(); ++j) {
            char c = line.charAt(j);
            if(c != ' ' && c != '\t') {
               key += c;
               offset = j;
            } else {
               break;
            }
         }
         key = key.trim();
         boolean started = false;
         for(int j = offset + 1; j < line.length(); ++j) {
            char c = line.charAt(j);
            if(c != ' ' && c != '\t') {
               started = true;
            }
            if(started) {
               value += c;
            }
         }
         value = value.trim();
         if(!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)) {
            indexes.put(key, ++i);
            result.put(key, value);
         }
      }
      return JSONObject.toJSONString(result);
   }

   private String processImage(File imageDir) {
      File[] imageFiles = imageDir.listFiles();
      if (imageFiles == null || imageFiles.length == 0) {
         return "";
      }
      JSONArray imageResult = new JSONArray();
      Arrays.stream(imageFiles).sorted(new Comparator<File>() {
         @Override
         public int compare(File o1, File o2) {
            return o1.getName().compareTo(o2.getName());
         }
      }).forEach(imageFile -> {
         Point size = aliOssUtil.imageSize(imageFile);
         if (size == null) {
            return;
         }
         JSONObject imageContent = new JSONObject();
         imageContent.put("width", size.x);
         imageContent.put("height", size.y);
         String url = aliOssUtil.uploadImage(imageFile);
         if (!StringUtils.isEmpty(url)) {
            imageContent.put("url", url);
            imageResult.add(imageContent);
         }
      });
      return JSON.toJSONString(imageResult);
   }

   public static void main(String[] args) {
      new JWResourceService().processVideoUpload(new File("D:/123.mp4"));
   }
}
